"""
页面锁管理器 - 提供页面级别的并发控制
"""

import threading
import time
from enum import Enum
from typing import Dict, Set, Optional, Tuple, List
from collections import defaultdict
import uuid
import weakref

# 添加当前目录到Python路径
import os
import sys
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
    sys.path.insert(0, current_dir)

try:
    from statistics import Logger
except ImportError:
    from .statistics import Logger


class LockType(Enum):
    """锁类型枚举"""
    SHARED = "shared"      # 共享锁（读锁）
    EXCLUSIVE = "exclusive"  # 排他锁（写锁）


class LockMode(Enum):
    """锁模式枚举"""
    READ = "read"          # 读模式
    WRITE = "write"        # 写模式


class PageLock:
    """页面锁对象"""
    
    def __init__(self, page_id: int, lock_type: LockType, holder_id: str):
        self.page_id = page_id
        self.lock_type = lock_type
        self.holder_id = holder_id
        self.acquired_time = time.time()
        self.reference_count = 1  # 引用计数，支持可重入锁
    
    def __str__(self):
        return f"PageLock(page={self.page_id}, type={self.lock_type.value}, holder={self.holder_id[:8]}...)"


class DeadlockDetector:
    """死锁检测器"""
    
    def __init__(self):
        self.wait_graph: Dict[str, Set[str]] = defaultdict(set)
        self.logger = Logger()
    
    def add_wait_edge(self, waiter: str, holder: str):
        """添加等待边：waiter等待holder释放锁"""
        self.wait_graph[waiter].add(holder)
        
    def remove_wait_edge(self, waiter: str, holder: str):
        """移除等待边"""
        if waiter in self.wait_graph:
            self.wait_graph[waiter].discard(holder)
            if not self.wait_graph[waiter]:
                del self.wait_graph[waiter]
    
    def detect_deadlock(self) -> Optional[Tuple[str, ...]]:
        """检测死锁，返回死锁环路中的事务ID"""
        visited = set()
        rec_stack = set()
        
        def has_cycle(node: str, path: list) -> Optional[Tuple[str, ...]]:
            if node in rec_stack:
                # 找到环路，返回环路中的节点
                cycle_start = path.index(node)
                return tuple(path[cycle_start:])
            
            if node in visited:
                return None
            
            visited.add(node)
            rec_stack.add(node)
            path.append(node)
            
            for neighbor in self.wait_graph.get(node, []):
                cycle = has_cycle(neighbor, path.copy())
                if cycle:
                    return cycle
            
            rec_stack.remove(node)
            return None
        
        # 检查所有节点
        for node in self.wait_graph:
            if node not in visited:
                cycle = has_cycle(node, [])
                if cycle:
                    self.logger.warn("DEADLOCK", 0, f"Deadlock detected: {' -> '.join(cycle)}")
                    return cycle
        
        return None
    
    def clear(self):
        """清空等待图"""
        self.wait_graph.clear()


class PageLockManager:
    """
    页面锁管理器
    
    提供页面级别的并发控制，支持：
    1. 共享锁和排他锁
    2. 锁升级和降级
    3. 死锁检测和预防
    4. 超时机制
    5. 可重入锁
    """
    
    def __init__(self, deadlock_detection=False, lock_timeout=2.0):
        """
        初始化页面锁管理器
        
        Args:
            deadlock_detection: 是否启用死锁检测
            lock_timeout: 锁超时时间（秒）
        """
        self.deadlock_detection = deadlock_detection
        self.lock_timeout = lock_timeout
        
        # 锁表：page_id -> {lock_type -> set of PageLock}
        self.page_locks: Dict[int, Dict[LockType, Set[PageLock]]] = defaultdict(
            lambda: defaultdict(set)
        )
        
        # 等待队列：page_id -> list of (lock_type, holder_id, condition)
        self.wait_queues: Dict[int, list] = defaultdict(list)
        
        # 持有者映射：holder_id -> set of page_ids
        self.holder_pages: Dict[str, Set[int]] = defaultdict(set)
        
        # 线程锁保护数据结构
        self._lock = threading.RLock()
        
        # 死锁检测器
        if self.deadlock_detection:
            self.deadlock_detector = DeadlockDetector()
        
        # 日志记录器
        self.logger = Logger()
        
        # 统计信息
        self.stats = {
            'locks_acquired': 0,
            'locks_released': 0,
            'lock_upgrades': 0,
            'lock_downgrades': 0,
            'deadlocks_detected': 0,
            'timeouts': 0
        }
        
        self.logger.info("INIT", 0, "Page lock manager initialized")
    
    def acquire_lock(self, page_id: int, lock_type: LockType, holder_id: Optional[str] = None) -> bool:
        """
        获取页面锁
        
        Args:
            page_id: 页面ID
            lock_type: 锁类型
            holder_id: 持有者ID，如果为None则生成一个
            
        Returns:
            bool: 是否成功获取锁
        """
        if holder_id is None:
            holder_id = str(uuid.uuid4())
        
        with self._lock:
            # 检查是否已经持有锁
            existing_lock = self._find_existing_lock(page_id, holder_id)
            if existing_lock:
                if existing_lock.lock_type == lock_type:
                    # 可重入锁，增加引用计数
                    existing_lock.reference_count += 1
                    self.logger.info("LOCK_REENTRANT", page_id, 
                                   f"Reentrant {lock_type.value} lock for {holder_id[:8]}...")
                    return True
                elif lock_type == LockType.EXCLUSIVE and existing_lock.lock_type == LockType.SHARED:
                    # 锁升级
                    return self._upgrade_lock(page_id, holder_id)
                elif lock_type == LockType.SHARED and existing_lock.lock_type == LockType.EXCLUSIVE:
                    # 已经持有排他锁，可以满足共享锁需求
                    self.logger.info("LOCK_DOWNGRADE", page_id, 
                                   f"Downgrade exclusive to shared for {holder_id[:8]}...")
                    return True
            
            # 检查是否可以立即获取锁
            if self._can_acquire_lock(page_id, lock_type, holder_id):
                self._grant_lock(page_id, lock_type, holder_id)
                self.stats['locks_acquired'] += 1
                return True
            
            # 需要等待，检查是否会造成死锁
            if self.deadlock_detection:
                conflicting_holders = self._get_conflicting_holders(page_id, lock_type)
                for conflicting_holder in conflicting_holders:
                    if conflicting_holder != holder_id:
                        self.deadlock_detector.add_wait_edge(holder_id, conflicting_holder)
                
                # 检测死锁
                cycle = self.deadlock_detector.detect_deadlock()
                if cycle and holder_id in cycle:
                    self.stats['deadlocks_detected'] += 1
                    self.logger.error("DEADLOCK", page_id, 
                                    f"Deadlock detected, aborting lock request for {holder_id[:8]}...")
                    # 清理等待边
                    for conflicting_holder in conflicting_holders:
                        if conflicting_holder != holder_id:
                            self.deadlock_detector.remove_wait_edge(holder_id, conflicting_holder)
                    return False
            
            # 添加到等待队列
            condition = threading.Condition(self._lock)
            self.wait_queues[page_id].append((lock_type, holder_id, condition))
            
            self.logger.info("LOCK_WAIT", page_id, 
                           f"Waiting for {lock_type.value} lock, holder: {holder_id[:8]}...")
            
            # 等待锁可用
            start_time = time.time()
            while not self._can_acquire_lock(page_id, lock_type, holder_id):
                remaining_time = self.lock_timeout - (time.time() - start_time)
                if remaining_time <= 0:
                    # 超时
                    self._remove_from_wait_queue(page_id, holder_id)
                    self.stats['timeouts'] += 1
                    self.logger.error("LOCK_TIMEOUT", page_id, 
                                    f"Lock timeout for {holder_id[:8]}...")
                    return False
                
                condition.wait(timeout=remaining_time)
            
            # 获取到锁
            self._remove_from_wait_queue(page_id, holder_id)
            self._grant_lock(page_id, lock_type, holder_id)
            self.stats['locks_acquired'] += 1
            
            # 清理死锁检测器中的等待边
            if self.deadlock_detection:
                conflicting_holders = self._get_conflicting_holders(page_id, lock_type)
                for conflicting_holder in conflicting_holders:
                    if conflicting_holder != holder_id:
                        self.deadlock_detector.remove_wait_edge(holder_id, conflicting_holder)
            
            return True
    
    def release_lock(self, page_id: int, holder_id: str, lock_type: Optional[LockType] = None) -> bool:
        """
        释放页面锁
        
        Args:
            page_id: 页面ID
            holder_id: 持有者ID
            lock_type: 锁类型，如果为None则释放该持有者的所有锁
            
        Returns:
            bool: 是否成功释放锁
        """
        with self._lock:
            page_lock_map = self.page_locks[page_id]
            released = False
            
            for lt, locks in page_lock_map.items():
                if lock_type is not None and lt != lock_type:
                    continue
                
                # 查找要释放的锁
                lock_to_remove = None
                for lock in locks:
                    if lock.holder_id == holder_id:
                        lock_to_remove = lock
                        break
                
                if lock_to_remove:
                    lock_to_remove.reference_count -= 1
                    if lock_to_remove.reference_count <= 0:
                        locks.remove(lock_to_remove)
                        self.holder_pages[holder_id].discard(page_id)
                        self.stats['locks_released'] += 1
                        released = True
                        
                        self.logger.info("LOCK_RELEASE", page_id, 
                                       f"Released {lt.value} lock for {holder_id[:8]}...")
                    else:
                        self.logger.info("LOCK_RELEASE", page_id, 
                                       f"Decremented {lt.value} lock reference count for {holder_id[:8]}...")
                        released = True
            
            if released:
                # 通知等待的线程
                self._notify_waiters(page_id)
                
                # 清理空的映射
                self._cleanup_empty_mappings(page_id)
            
            return released
    
    def release_all_locks(self, holder_id: str) -> int:
        """
        释放持有者的所有锁
        
        Args:
            holder_id: 持有者ID
            
        Returns:
            int: 释放的锁数量
        """
        with self._lock:
            pages_to_release = list(self.holder_pages[holder_id])
            released_count = 0
            
            for page_id in pages_to_release:
                if self.release_lock(page_id, holder_id):
                    released_count += 1
            
            self.logger.info("LOCK_RELEASE_ALL", 0, 
                           f"Released {released_count} locks for {holder_id[:8]}...")
            
            return released_count
    
    def _find_existing_lock(self, page_id: int, holder_id: str) -> Optional[PageLock]:
        """查找已存在的锁"""
        page_lock_map = self.page_locks[page_id]
        for locks in page_lock_map.values():
            for lock in locks:
                if lock.holder_id == holder_id:
                    return lock
        return None
    
    def _can_acquire_lock(self, page_id: int, lock_type: LockType, holder_id: str) -> bool:
        """检查是否可以获取锁"""
        page_lock_map = self.page_locks[page_id]
        
        if lock_type == LockType.SHARED:
            # 共享锁：只要没有排他锁就可以获取
            exclusive_locks = page_lock_map[LockType.EXCLUSIVE]
            for lock in exclusive_locks:
                if lock.holder_id != holder_id:
                    return False
            return True
        
        elif lock_type == LockType.EXCLUSIVE:
            # 排他锁：不能有任何其他锁
            for locks in page_lock_map.values():
                for lock in locks:
                    if lock.holder_id != holder_id:
                        return False
            return True
        
        return False
    
    def _get_conflicting_holders(self, page_id: int, lock_type: LockType) -> Set[str]:
        """获取冲突的锁持有者"""
        page_lock_map = self.page_locks[page_id]
        conflicting_holders = set()
        
        if lock_type == LockType.SHARED:
            # 共享锁与排他锁冲突
            for lock in page_lock_map[LockType.EXCLUSIVE]:
                conflicting_holders.add(lock.holder_id)
        
        elif lock_type == LockType.EXCLUSIVE:
            # 排他锁与所有锁冲突
            for locks in page_lock_map.values():
                for lock in locks:
                    conflicting_holders.add(lock.holder_id)
        
        return conflicting_holders
    
    def _grant_lock(self, page_id: int, lock_type: LockType, holder_id: str):
        """授予锁"""
        lock = PageLock(page_id, lock_type, holder_id)
        self.page_locks[page_id][lock_type].add(lock)
        self.holder_pages[holder_id].add(page_id)
        
        self.logger.info("LOCK_GRANT", page_id, 
                       f"Granted {lock_type.value} lock to {holder_id[:8]}...")
    
    def _upgrade_lock(self, page_id: int, holder_id: str) -> bool:
        """锁升级：从共享锁升级到排他锁"""
        page_lock_map = self.page_locks[page_id]
        shared_locks = page_lock_map[LockType.SHARED]
        
        # 检查是否只有当前持有者持有共享锁
        other_shared_holders = [lock.holder_id for lock in shared_locks if lock.holder_id != holder_id]
        if other_shared_holders:
            self.logger.warn("LOCK_UPGRADE", page_id, 
                           f"Cannot upgrade lock: other shared holders exist")
            return False
        
        # 移除共享锁，添加排他锁
        lock_to_upgrade = None
        for lock in shared_locks:
            if lock.holder_id == holder_id:
                lock_to_upgrade = lock
                break
        
        if lock_to_upgrade:
            shared_locks.remove(lock_to_upgrade)
            self._grant_lock(page_id, LockType.EXCLUSIVE, holder_id)
            self.stats['lock_upgrades'] += 1
            
            self.logger.info("LOCK_UPGRADE", page_id, 
                           f"Upgraded to exclusive lock for {holder_id[:8]}...")
            return True
        
        return False
    
    def _remove_from_wait_queue(self, page_id: int, holder_id: str):
        """从等待队列中移除"""
        wait_queue = self.wait_queues[page_id]
        self.wait_queues[page_id] = [
            (lt, hid, cond) for lt, hid, cond in wait_queue if hid != holder_id
        ]
    
    def _notify_waiters(self, page_id: int):
        """通知等待者"""
        wait_queue = self.wait_queues[page_id]
        for lock_type, holder_id, condition in wait_queue:
            if self._can_acquire_lock(page_id, lock_type, holder_id):
                condition.notify()
    
    def _cleanup_empty_mappings(self, page_id: int):
        """清理空的映射"""
        page_lock_map = self.page_locks[page_id]
        empty_types = [lt for lt, locks in page_lock_map.items() if not locks]
        for lt in empty_types:
            del page_lock_map[lt]
        
        if not page_lock_map:
            del self.page_locks[page_id]
        
        if not self.wait_queues[page_id]:
            del self.wait_queues[page_id]
    
    def get_lock_info(self, page_id: int) -> Dict:
        """获取页面锁信息"""
        with self._lock:
            page_lock_map = self.page_locks[page_id]
            lock_info = {
                'shared_locks': [],
                'exclusive_locks': [],
                'waiters': []
            }
            
            for lock in page_lock_map[LockType.SHARED]:
                lock_info['shared_locks'].append({
                    'holder_id': lock.holder_id[:8] + "...",
                    'acquired_time': lock.acquired_time,
                    'reference_count': lock.reference_count
                })
            
            for lock in page_lock_map[LockType.EXCLUSIVE]:
                lock_info['exclusive_locks'].append({
                    'holder_id': lock.holder_id[:8] + "...",
                    'acquired_time': lock.acquired_time,
                    'reference_count': lock.reference_count
                })
            
            for lock_type, holder_id, _ in self.wait_queues[page_id]:
                lock_info['waiters'].append({
                    'holder_id': holder_id[:8] + "...",
                    'requested_type': lock_type.value
                })
            
            return lock_info
    
    def get_stats(self) -> Dict:
        """获取统计信息"""
        with self._lock:
            stats = self.stats.copy()
            stats['current_locks'] = sum(
                len(locks) for page_locks in self.page_locks.values() 
                for locks in page_locks.values()
            )
            stats['waiting_threads'] = sum(
                len(waiters) for waiters in self.wait_queues.values()
            )
            return stats
    
    def print_lock_status(self):
        """打印锁状态"""
        with self._lock:
            print(f"\n{'='*60}")
            print(f"页面锁管理器状态")
            print(f"{'='*60}")
            
            # 统计信息
            stats = self.get_stats()
            print(f"统计信息:")
            print(f"  当前锁数量: {stats['current_locks']}")
            print(f"  等待线程数: {stats['waiting_threads']}")
            print(f"  已获取锁: {stats['locks_acquired']}")
            print(f"  已释放锁: {stats['locks_released']}")
            print(f"  锁升级: {stats['lock_upgrades']}")
            print(f"  锁降级: {stats['lock_downgrades']}")
            print(f"  死锁检测: {stats['deadlocks_detected']}")
            print(f"  超时次数: {stats['timeouts']}")
            print()
            
            # 当前锁状态
            if self.page_locks:
                print(f"当前锁状态:")
                for page_id, page_lock_map in self.page_locks.items():
                    print(f"  页面 {page_id}:")
                    for lock_type, locks in page_lock_map.items():
                        if locks:
                            print(f"    {lock_type.value.upper()} 锁:")
                            for lock in locks:
                                print(f"      持有者: {lock.holder_id[:12]}... "
                                     f"引用计数: {lock.reference_count}")
            else:
                print("当前锁状态: 无锁")
            
            print(f"{'='*60}")


class ThreadSafeLockManager:
    """线程安全的锁管理器"""
    
    def __init__(self, enable_distributed: bool = False):
        self.page_lock_manager = PageLockManager()
        self.enable_distributed = enable_distributed
        
        # 线程到事务ID的映射（支持显式事务管理）
        self._thread_transactions: Dict[int, str] = {}
        self._transaction_threads: Dict[str, int] = {}
        self._mapping_lock = threading.RLock()
        
        # 活跃事务管理
        self._active_transactions: Dict[str, dict] = {}
        self._transaction_lock = threading.RLock()
        
        # 全局锁状态（跨线程共享）
        self._global_lock_state = threading.RLock()
        
    def begin_transaction(self, transaction_id: str = None) -> str:
        """开始新事务"""
        if transaction_id is None:
            transaction_id = str(uuid.uuid4())
        
        thread_id = threading.get_ident()
        
        with self._mapping_lock:
            # 检查线程是否已有事务
            if thread_id in self._thread_transactions:
                old_txn = self._thread_transactions[thread_id]
                self._cleanup_transaction(old_txn)
            
            # 绑定事务到线程
            self._thread_transactions[thread_id] = transaction_id
            self._transaction_threads[transaction_id] = thread_id
        
        with self._transaction_lock:
            self._active_transactions[transaction_id] = {
                'thread_id': thread_id,
                'start_time': time.time(),
                'locks_held': set(),
                'operations_count': 0
            }
        
        return transaction_id
    
    def commit_transaction(self, transaction_id: str = None) -> bool:
        """提交事务"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        if not transaction_id:
            return False
        
        try:
            # 释放事务持有的所有锁
            released_count = self.page_lock_manager.release_all_locks(transaction_id)
            
            # 清理事务状态
            self._cleanup_transaction(transaction_id)
            
            return True
            
        except Exception as e:
            # 回滚时也要释放锁
            self.page_lock_manager.release_all_locks(transaction_id)
            self._cleanup_transaction(transaction_id)
            raise
    
    def rollback_transaction(self, transaction_id: str = None):
        """回滚事务"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        if transaction_id:
            # 释放所有锁
            self.page_lock_manager.release_all_locks(transaction_id)
            self._cleanup_transaction(transaction_id)
    
    def _cleanup_transaction(self, transaction_id: str):
        """清理事务状态"""
        with self._mapping_lock:
            if transaction_id in self._transaction_threads:
                thread_id = self._transaction_threads[transaction_id]
                self._thread_transactions.pop(thread_id, None)
                del self._transaction_threads[transaction_id]
        
        with self._transaction_lock:
            self._active_transactions.pop(transaction_id, None)
    
    def _get_transaction_id(self) -> Optional[str]:
        """获取当前线程的事务ID"""
        thread_id = threading.get_ident()
        
        with self._mapping_lock:
            transaction_id = self._thread_transactions.get(thread_id)
            
            if not transaction_id:
                # 自动创建隐式事务
                transaction_id = self.begin_transaction()
            
            return transaction_id
    
    def acquire_read_lock(self, page_id: int, transaction_id: str = None) -> bool:
        """获取读锁"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        with self._global_lock_state:
            success = self.page_lock_manager.acquire_lock(
                page_id, LockType.SHARED, transaction_id
            )
            
            if success:
                with self._transaction_lock:
                    if transaction_id in self._active_transactions:
                        self._active_transactions[transaction_id]['locks_held'].add(page_id)
                        self._active_transactions[transaction_id]['operations_count'] += 1
            
            return success
    
    def acquire_write_lock(self, page_id: int, transaction_id: str = None) -> bool:
        """获取写锁"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        with self._global_lock_state:
            success = self.page_lock_manager.acquire_lock(
                page_id, LockType.EXCLUSIVE, transaction_id
            )
            
            if success:
                with self._transaction_lock:
                    if transaction_id in self._active_transactions:
                        self._active_transactions[transaction_id]['locks_held'].add(page_id)
                        self._active_transactions[transaction_id]['operations_count'] += 1
            
            return success
    
    def release_lock(self, page_id: int, transaction_id: str = None) -> bool:
        """释放锁"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        with self._global_lock_state:
            success = self.page_lock_manager.release_lock(page_id, transaction_id)
            
            if success:
                with self._transaction_lock:
                    if transaction_id in self._active_transactions:
                        self._active_transactions[transaction_id]['locks_held'].discard(page_id)
            
            return success
    
    def release_all_locks(self, transaction_id: str = None) -> int:
        """释放事务的所有锁"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        with self._global_lock_state:
            released_count = self.page_lock_manager.release_all_locks(transaction_id)
            
            with self._transaction_lock:
                if transaction_id in self._active_transactions:
                    self._active_transactions[transaction_id]['locks_held'].clear()
            
            return released_count
    
    def get_transaction_info(self, transaction_id: str = None) -> Optional[dict]:
        """获取事务信息"""
        if transaction_id is None:
            transaction_id = self._get_transaction_id()
        
        with self._transaction_lock:
            return self._active_transactions.get(transaction_id, {}).copy()
    
    def get_active_transactions(self) -> Dict[str, dict]:
        """获取所有活跃事务"""
        with self._transaction_lock:
            return {
                txn_id: info.copy() 
                for txn_id, info in self._active_transactions.items()
            }
    
    def detect_deadlocks(self) -> List[Tuple[str, ...]]:
        """检测所有活跃事务的死锁"""
        return [self.page_lock_manager.deadlock_detector.detect_deadlock()] if \
               self.page_lock_manager.deadlock_detection else []


class LockManager(ThreadSafeLockManager):
    """兼容性别名，保持向后兼容"""
    
    def __init__(self):
        super().__init__(enable_distributed=False)
        self._thread_local = threading.local()
    
    def _get_transaction_id(self) -> str:
        """获取当前线程的事务ID（兼容模式）"""
        if not hasattr(self._thread_local, 'transaction_id'):
            self._thread_local.transaction_id = str(uuid.uuid4())
        return self._thread_local.transaction_id
